<?php

namespace yogurt\db;

use PDO;
use PDOException;

/**
 * Class Builder
 * @package yogurt\db
 * @method \yogurt\db\Builder where(mixed $field, string $op = null, mixed $condition = null) 查询条件
 * @method \yogurt\db\Builder whereOr(mixed $field, string $op = null, mixed $condition = null) OR查询条件
 * @method \yogurt\db\Builder insert(array $data) 添加数据
 * @method \yogurt\db\Builder update(array $data) 更新数据
 * @method \yogurt\db\Builder insertAll(array $data) 批量添加数据
 * @method \yogurt\db\Builder insertGetId(array $data) 添加数据并返回主键值
 * @method \yogurt\db\Builder select() 查询多条数据函数
 * @method \yogurt\db\Builder find() 查询单条数据函数
 * @method \yogurt\db\Builder count() 查询统计数据
 * @method \yogurt\db\Builder delete($model = false) 删除数据
 * @method \yogurt\db\Builder union(string $option) 合并查询
 * @method \yogurt\db\Builder field(string $field) 查询指定字段数据
 * @method \yogurt\db\Builder order(string $option) 排序查询
 * @method \yogurt\db\Builder limit(int $page, int $pageSize) 分页查询
 * @method \yogurt\db\Builder lock(string $option) 查询锁定数据库
 * @method \yogurt\db\Builder comment(string $option) 添加sql注释
 * @method \yogurt\db\Builder distinct(bool $option) 数据去重
 * @method \yogurt\db\Builder group(string $option) 数据分组查询
 * @method \yogurt\db\Builder force(string $option) 强制使用索引
 * @method \yogurt\db\Builder fetchSql() 打印sql
 * @method \yogurt\db\Builder value(string $field) 获取指定字段值
 * @method \yogurt\db\Builder alias(string $alias) 数据库表别名
 * @method \yogurt\db\Builder cache(int $expire) 缓存查询
 * @method \yogurt\db\Builder join(string $option, string $where, $model = 'left') 数据库表连接
 * @method \yogurt\db\Builder setInc(string $filed, int $number = 1) 字段自增
 * @method \yogurt\db\Builder setDec(string $filed, int $number = 1) 字段自减
 */
abstract class Builder
{
    protected $config = [];

    protected $links = [];

    protected $option = [];

    protected $master = false;


    /**
     * 连接数据库的方法
     * @return mixed
     */
    public function connection()
    {
        if (!isset($this->links[$this->master])) {
            try {
                $dbh = new PDO($this->option['dsn'], $this->option['username'], $this->option['password'], []);
                $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);  //设置如果sql语句执行错误则抛出异常，事务会自动回滚
                $dbh->setAttribute(PDO::ATTR_EMULATE_PREPARES, false); //禁用prepared statements的仿真效果(防SQL注入)
                $dbh->exec('SET NAMES ' . $this->option['charset']);
                $this->links[$this->master] = $dbh;
            } catch (PDOException $e) {
                die('Connection failed: ' . $e->getMessage());
            }
        }
        return $this->links[$this->master];

    }

    /**
     * 解析PDO连接地址
     * @param $config
     * @return string
     */
    public function parseDsn($config)
    {

        if (!empty($config['socket'])) {
            $dsn = 'mysql:unix_socket=' . $config['socket'];
        } elseif (!empty($config['hostport'])) {
            $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'];
        } else {
            $dsn = 'mysql:host=' . $config['hostname'];
        }
        $dsn .= ';dbname=' . $config['database'];

        if (!empty($config['charset'])) {
            $dsn .= ';charset=' . $config['charset'];
        }
        return $dsn;
    }

    /**
     * 根据操作类型获取数据库信息
     * @param bool $master
     */
    public function readMaster($master = false)
    {

        $this->option = $this->config;
        $hosts = explode(',', $this->config['hostname']);
        $passwords = explode(',', $this->config['password']);

        if ($this->config['deploy']) { // 开启分布式数据库
            if ($master) {
                $masterHost = array_slice($hosts, 0, $this->config['master_number']);
            } else {
                $masterHost = array_slice($hosts, $this->config['master_number']);
            }
            $index = array_rand($masterHost);
            $host = $masterHost[$index];
            $password = $passwords[$index] ?? $passwords[0];

        } else { // 不开启分布式
            $host = $hosts[0];
            $password = $passwords[0];

        }

        $this->option['hostname'] = $host;
        $this->option['password'] = $password;
        $this->option['dsn'] = $this->parseDsn($this->option);
        $this->connection();

    }


}
